package com.geqian.structure.utils;

import com.geqian.structure.annotation.TableField;
import com.geqian.structure.entity.TableColumnInfo;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTInd;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblBorders;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STBorder;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author geqian
 * @date 22:38 2023/5/31
 */
public class WordUtils {

    public static class WordBytesBuilder {

        private final XWPFDocument document;

        private WordBytesBuilder(XWPFDocument document) {
            this.document = document;
        }


        /**
         * 换行
         *
         * @return
         */
        public WordBytesBuilder setCarriageReturn() {
            //创建段落
            document.createParagraph();
            // 对齐方式 paragraph.setAlignment(ParagraphAlignment.CENTER);
            return this;
        }

        /**
         * 添加段落文字
         *
         * @return
         */
        public WordBytesBuilder addParagraphText(String text) {
            addParagraphText(text, new ParagraphTextConfig());
            return this;
        }

        /**
         * 添加段落文本
         *
         * @return
         */
        public WordBytesBuilder addParagraphText(String text, ParagraphTextConfig config) {
            //创建段落
            XWPFParagraph paragraph = document.createParagraph();
            addParagraphText(paragraph, text, config);
            return this;
        }

        /**
         * 添加段落文本
         *
         * @return
         */
        public void addParagraphText(XWPFParagraph paragraph, String text, ParagraphTextConfig config) {
            // 对齐方式 paragraph.setAlignment(ParagraphAlignment.CENTER);
            //创建段落文本
            XWPFRun run = paragraph.createRun();
            // 颜色 run.setColor("00ff00");
            // 斜体 run.setItalic(true);
            // 粗体 run.setBold(true);
            // 字体 run.setFontFamily("Courier");
            // 下划线 run.setUnderline(UnderlinePatterns.DOT_DOT_DASH);
            run.setText(text);
            run.setBold(config.isBlod());
            run.setItalic(config.isItalic());
            run.setFontSize(config.getFontSize());
            run.setColor(config.getColor());
            run.setFontFamily(config.getFontFamily());
            setFirstLineIndent(paragraph, config.getIndent());
            if (Objects.equals(true, config.isCarriageReturn())) {
                run.addBreak();
            }
        }

        /**
         * 设置段落首行缩进字符个数
         *
         * @param paragraph
         * @param indent
         */
        private void setFirstLineIndent(XWPFParagraph paragraph, int indent) {
            // 设置段落的首行缩进
            CTPPr ppr = paragraph.getCTP().getPPr();
            if (ppr == null) {
                ppr = paragraph.getCTP().addNewPPr();
            }
            CTInd ind = ppr.getInd();
            if (ind == null) {
                ind = ppr.addNewInd();
            }
            ind.setFirstLineChars(BigInteger.valueOf(100L * indent));
        }

        /**
         * 添加图片
         *
         * @param bytes
         * @return
         * @throws Exception
         */
        public WordBytesBuilder addPicture(byte[] bytes, String filename, int width, int height) throws Exception {
            InputStream stream = new ByteArrayInputStream(bytes);
            XWPFParagraph paragraph = document.createParagraph();
            XWPFRun run = paragraph.createRun();
            run.addPicture(stream, XWPFDocument.PICTURE_TYPE_PNG, filename, Units.toEMU(width), Units.toEMU(height));
            return this;
        }


        /**
         * 添加表格
         *
         * @param dataList 数据列表
         */
        public WordBytesBuilder addTable(List<?> dataList) {
            return addTable(dataList, new ParagraphTextConfig());
        }


        /**
         * 添加表格
         *
         * @param dataList 数据列表
         */
        public WordBytesBuilder addTable(List<?> dataList, ParagraphTextConfig config) {
            if (dataList != null && dataList.size() > 0) {
                // 表格行数
                int rows = dataList.size() + 1;

                List<Field> fieldList = Arrays.stream(dataList.get(0).getClass().getDeclaredFields())
                        .filter(field -> Objects.nonNull(field.getAnnotation(TableField.class)) && TableColumnInfo.getFieldList().contains(field.getName()))
                        .collect(Collectors.toList());
                //表格列数
                int columns = fieldList.size();

                XWPFTable table = document.createTable(rows, columns);
                // 设置表格对齐方式
                table.setCellMargins(70, 0, 70, 0);
                table.setTableAlignment(TableRowAlign.LEFT);
                // 设置表格样式
                table.getCTTbl().addNewTblPr().addNewTblW().setW(BigInteger.valueOf(8000));
                table.getCTTbl().getTblPr().unsetTblBorders();

                // 设置表格边框线条类型
                CTTblBorders borders = table.getCTTbl().addNewTblPr().addNewTblBorders();
                borders.addNewTop().setVal(STBorder.SINGLE);
                borders.addNewBottom().setVal(STBorder.SINGLE);
                borders.addNewLeft().setVal(STBorder.SINGLE);
                borders.addNewRight().setVal(STBorder.SINGLE);
                borders.addNewInsideH().setVal(STBorder.SINGLE);
                borders.addNewInsideV().setVal(STBorder.SINGLE);
                //获取表格数据
                List<List<String>> tableRowDataList = getTableRowDataList(dataList, fieldList);
                for (int i = 0; i < rows; i++) {
                    XWPFTableRow row = table.getRow(i);
                    for (int j = 0; j < columns; j++) {
                        XWPFParagraph paragraph = row.getCell(j).getParagraphs().get(0);
                        addParagraphText(paragraph, String.valueOf(tableRowDataList.get(i).get(j)), config);
                    }
                }
            }
            return this;
        }


        /**
         * 构建 word字节数组
         *
         * @return
         * @throws Exception
         */
        public byte[] build() throws Exception {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            document.write(out);
            document.close();
            out.close();
            return out.toByteArray();
        }


        /**
         * 获取word表格每一行数据
         *
         * @param dataList 数据
         * @return
         */
        private List<List<String>> getTableRowDataList(List<?> dataList, List<Field> fieldList) {
            List<List<String>> tableRowDataList = null;
            if (dataList != null && dataList.size() > 0) {

                //通过注解获取需要输出的属性别名
                List<String> tableHeaderList = fieldList.stream()
                        .sorted(Comparator.comparing(field -> field.getAnnotation(TableField.class).order()))
                        .map(field -> field.getAnnotation(TableField.class).value())
                        .collect(Collectors.toList());

                //获取dataList中每个对象的属性值，并转换为List
                tableRowDataList = dataList.stream().map(data ->
                        fieldList.stream().map(field -> {
                            field.setAccessible(true);
                            try {
                                return String.valueOf(field.get(data));
                            } catch (Exception e) {
                                return "";
                            }
                        }).collect(Collectors.toList())
                ).collect(Collectors.toCollection(LinkedList::new));

                tableRowDataList.add(0, tableHeaderList);
            }
            return tableRowDataList != null ? tableRowDataList : Collections.emptyList();
        }

    }

    /**
     * 创建一个WordBuilder
     *
     * @return
     */
    public static WordBytesBuilder createWordBytesBuilder() {
        return new WordBytesBuilder(new XWPFDocument());
    }

    /**
     * 创建一个 wordConfig
     *
     * @return
     */
    public static ParagraphTextConfig createParagraphTextConfig() {
        return new ParagraphTextConfig();
    }

    /**
     * 下载word到浏览器
     *
     * @param bytes    word字节数组
     * @param response 响应
     */
    public static void download(byte[] bytes, String fileName, HttpServletResponse response) throws Exception {
        //out为OutputStream，需要写入到的目标流
        ServletOutputStream out = response.getOutputStream();
        //二进制数组写入输出流
        out.write(bytes);

        response.setHeader("filename", URLEncoder.encode(fileName + ".docx", "UTF-8"));

        response.setHeader("Access-Control-Expose-Headers", "filename");

        response.setHeader("content-type", "application/octet-stream");

        //文件设置为附件
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".docx", "UTF-8"));

        out.close();

    }


    /**
     * 下载word到本地
     *
     * @param bytes      word字节数组
     * @param targetPath 保存路径
     * @throws Exception
     */
    public static void download(byte[] bytes, String targetPath) throws Exception {
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetPath));
        //二进制数组写入输出流
        bos.write(bytes);
        bos.flush();
        bos.close();
    }


    public static class ParagraphTextConfig {
        //文字大小
        private Integer fontSize = 12;
        //首行缩进字符个数
        private Integer indent = 0;
        //文字颜色 16进制格式
        private String color = "000000";
        //文字类型
        private String fontFamily = "Arial";
        //是否加粗
        private boolean blod = false;
        //是否换行
        private boolean carriageReturn = false;
        //是否斜体
        private boolean italic = false;

        private ParagraphTextConfig() {
        }

        public Integer getFontSize() {
            return fontSize;
        }

        public ParagraphTextConfig setFontSize(Integer fontSize) {
            this.fontSize = fontSize;
            return this;
        }

        public Integer getIndent() {
            return indent;
        }

        public ParagraphTextConfig setIndent(Integer indent) {
            this.indent = indent;
            return this;
        }

        public String getColor() {
            return color;
        }

        public ParagraphTextConfig setColor(String color) {
            this.color = color;
            return this;
        }

        public String getFontFamily() {
            return fontFamily;
        }

        public ParagraphTextConfig setFontFamily(String fontFamily) {
            this.fontFamily = fontFamily;
            return this;
        }

        public boolean isBlod() {
            return blod;
        }

        public ParagraphTextConfig setBlod(boolean blod) {
            this.blod = blod;
            return this;
        }

        public boolean isCarriageReturn() {
            return carriageReturn;
        }

        public ParagraphTextConfig setCarriageReturn(boolean carriageReturn) {
            this.carriageReturn = carriageReturn;
            return this;
        }

        public boolean isItalic() {
            return italic;
        }

        public ParagraphTextConfig setItalic(boolean italic) {
            this.italic = italic;
            return this;
        }

        @Override
        public String toString() {
            return "ParagraphTextConfig{" +
                    "fontSize=" + fontSize +
                    ", indent=" + indent +
                    ", color='" + color + '\'' +
                    ", fontFamily='" + fontFamily + '\'' +
                    ", blod=" + blod +
                    ", carriageReturn=" + carriageReturn +
                    ", italic=" + italic +
                    '}';
        }
    }

    public static void main(String[] args) throws Exception {
        WordBytesBuilder wordBytesBuilder = WordUtils.createWordBytesBuilder();
        ParagraphTextConfig textConfig = WordUtils.createParagraphTextConfig();
        System.out.println(textConfig);
        byte[] bytes = wordBytesBuilder
                .addParagraphText("默认段落文字样式", textConfig)
                .setCarriageReturn()
                .addParagraphText("更改样式后的段落文字!", textConfig.setBlod(false).setColor("fcc422"))
                .build();
        FileOutputStream fileOutputStream = new FileOutputStream("d:\\desktop\\demo.docx");
        fileOutputStream.write(bytes);
    }

}
